/*
*    Phantom v2.0 GUI Automation Software
*    Copyright (C) 2009  John C. Van Arsdall (john@phantomtest.com)
*
*    This program is free software; you can redistribute it and/or modify
*    it under the terms of the GNU General Public License as published by
*    the Free Software Foundation; either version 2 of the License, or
*    (at your option) any later version.
*
*    This program is distributed in the hope that it will be useful,
*    but WITHOUT ANY WARRANTY; without even the implied warranty of
*    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
*    GNU General Public License for more details.
*
*    You should have received a copy of the GNU General Public License along
*    with this program; if not, write to the Free Software Foundation, Inc.,
*    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
*/
// Minion.cpp : Defines the initialization routines for the DLL.
//
//#define MINIONLIBAPI extern "C" __declspec(dllexport)
#define TEXT_MAX 255
//#define MINION_EXPORTS



// Memory leak stuff
#define CRTDBG_MAP_ALLOC
#include <stdlib.h>
#include <crtdbg.h>

#include "resource.h"
#include "stdafx.h"
#include "Minion.h"
#include "Minion_External.h"
#include "MemoryMapper.h"
#include "FunctionProcessor.h"
#include "DataMap.h"
#include ".\minion.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#endif

#pragma data_seg("Shared")
HHOOK g_hHook = SSI_NULL;
DWORD g_dwThreadIdPHANTOM = 0;
DWORD g_Result = 0;
DWORD g_CreateMinion = 0;  // Deprecated...
static HWND hwndMINION = SSI_NULL;
int bALL_DATA_RECEIVED = 0;
int bMINION_MSG_FLAGS = 0;
int bMINION_SYNC = 0;
#pragma data_seg()
#pragma comment(linker, "/section:Shared,rws")

HINSTANCE g_hinstDll = SSI_NULL;
HANDLE hMap = SSI_NULL;

CDataMap DataMap;

int CreateMinionWindow(DWORD dwThreadId);

#define FLG_FUNC_TYPE    1
#define FLG_INT_TYPE     2
#define FLG_STRING_TYPE  4
#define FLG_REAL_TYPE    8
#define FLG_VOID_TYPE    16
#define FLG_BOOL_TYPE    32



MINION_API int LocalAddMap(int RemoteType, int LocalType){
	return DataMap.AddMap(RemoteType, LocalType);
}

MINION_API void MinionLog(const char* Tag){
#if(0)
	FILE* FH;
	FH = fopen("c:\\minion.log", "a");
	if(!FH) return;
	fprintf(FH, "%s\n", Tag);
	fclose(FH);
#endif
}
MINION_API void MinionClear(){
#if(0)
	FILE* FH;
	FH = fopen("c:\\minion.log", "w");
	if(!FH) return;
	fclose(FH);
#endif
}

//
//	Note!
//
//		If this DLL is dynamically linked against the MFC
//		DLLs, any functions exported from this DLL which
//		call into MFC must have the AFX_MANAGE_STATE macro
//		added at the very beginning of the function.
//
//		For example:
//
//		extern "C" BOOL PASCAL EXPORT ExportedFunction()
//		{
//			AFX_MANAGE_STATE(AfxGetStaticModuleState());
//			// normal function body here
//		}
//
//		It is very important that this macro appear in each
//		function, prior to any calls into MFC.  This means that
//		it must appear as the first statement within the 
//		function, even before any object variable declarations
//		as their constructors may generate calls into the MFC
//		DLL.
//
//		Please see MFC Technical Notes 33 and 58 for additional
//		details.
//

// CMinionApp

BEGIN_MESSAGE_MAP(CMinionApp, CWinApp)
END_MESSAGE_MAP()


// CMinionApp construction

CMinionApp::CMinionApp()
{
	// TODO: add construction code here,
	// Place all significant initialization in InitInstance
}

// The one and only CMinionApp object

CMinionApp theApp;

// CMinionApp initialization

BOOL CMinionApp::InitInstance()
{

  MinionLog("InitInstance MINION");
	CWinApp::InitInstance();
	g_hinstDll = AfxGetInstanceHandle();

	// This is the new initialization
	//  Can we send a message to the app
	//  to set the hook?
	/* Perhaps the process can be:
			1. Set the hook
			2. Setup Shared Mem (within hook?)
			3. In the caller, write the object (CDataWindow) to the
			   shared mem (need a write routine)
		  4. Send message to app to read from shared mem to 
			   get the CDataWindow (need function API too)
		  5. Perform the function
			6. Write the CDataWindow to the Shared mem again
			7. Return status from message
			8. In caller, read the Shared mem to get CDataWindow
			9. Delete the shared mem
			10.  Remove the hook
	*/

	return TRUE;
}

// Deprecated...
void MINION_OnClose(HWND hWnd)
{
  MinionLog("Closing MINION");
  if(USE_MINION_WIN){
	  DestroyWindow(hWnd);
  }
}

CData* ReadDataObject(char* thePtr, SSI_INT64& BytesRead){
	SSI_INT64 Type = 0;
	SSI_INT64 StringSize = 0;
	CData* Ret = 0;
	BytesRead = 0;

	CString s;

	memcpy(&Type, thePtr, sizeof(Type));
	BytesRead += sizeof(Type);

	// Map it to the type in this local process
	Type = DataMap.GetLocalType(Type);

	if(Type == CDataString::GetRegisteredType()){

		memcpy(&StringSize, thePtr+BytesRead, sizeof(StringSize));
		BytesRead += sizeof(StringSize);
		char* str = new char[StringSize+1];
		memcpy(str, thePtr+BytesRead, StringSize);
		BytesRead = BytesRead + StringSize;
		str[StringSize] = 0;
		Ret = Factory_MakeData(Type);
		if(!Ret){
			return Ret;
		}
		((CDataString*)Ret)->Data = str;
		delete [] str;
		return Ret;
	}else if(Type == CDataInt::GetRegisteredType()){
		SSI_INT64 Data;
		memcpy(&Data, thePtr+BytesRead, sizeof(Data));
		BytesRead += sizeof(Data);
		Ret = Factory_MakeData(Type);
		((CDataInt*)Ret)->Data = Data;
		return Ret;
	}else if(Type == CDataReal::GetRegisteredType()){
		REAL Data;
		memcpy(&Data, thePtr+BytesRead, sizeof(Data));
		BytesRead += sizeof(Data);
		Ret = Factory_MakeData(Type);
		((CDataReal*)Ret)->Data = Data;
		return Ret;
	}else if(Type == CDataBool::GetRegisteredType()){
		SSI_BOOL Data;
		memcpy(&Data, thePtr+BytesRead, sizeof(Data));
		BytesRead += sizeof(Data);
		Ret = Factory_MakeData(Type);
		((CDataBool*)Ret)->Data = Data;
		return Ret;
	}else if(Type == CDataVoid::GetRegisteredType()){
		// Return an empty void
		Ret = Factory_MakeData(Type);
		return Ret;
	}else{
		MinionLog("Data Type Error");
		s.Format("%d", Type);
		//MessageBox(SSI_NULL, (LPCTSTR)s, "MINION: Unknown Data Type", MB_OK);
		return 0;
	}
	return 0;
}

int WriteDataObject(char* thePtr, CData* In){
	SSI_INT64 RemoteType = 0;
	SSI_INT64 Type = In->GetType();
	SSI_INT64 StringSize = 0;
	SSI_INT64 BytesWrote = 0;
	CVariable JunkVar;

	BytesWrote = 0;

	// Map it to the type in this local process
	RemoteType = DataMap.GetRemoteType(Type);

	memcpy(thePtr, &RemoteType, sizeof(RemoteType));
	BytesWrote += sizeof(RemoteType);

	if(Type == CDataString::GetRegisteredType()){
		StringSize = ((CDataString*)In)->Data.GetLength();
    if(StringSize >= (MAP_FILE_SIZE - BytesWrote - 1000)){
      StringSize = (MAP_FILE_SIZE - BytesWrote - 1000);
      
    }
		memcpy(thePtr+BytesWrote, &StringSize, sizeof(StringSize));
		BytesWrote = BytesWrote + sizeof(StringSize);
		memcpy(thePtr+BytesWrote, (void*)(((CDataString*)In)->Data.Str()), StringSize);
		BytesWrote = BytesWrote + StringSize;
		return BytesWrote;
	}else if(Type == CDataInt::GetRegisteredType()){
		SSI_INT64 Data = ((CDataInt*)In)->Data;
		memcpy(thePtr+BytesWrote, &Data, sizeof(Data));
		BytesWrote += sizeof(Data);
		return BytesWrote;
	}else if(Type == CDataReal::GetRegisteredType()){
		REAL Data = ((CDataReal*)In)->Data;
		memcpy(thePtr+BytesWrote, &Data, sizeof(Data));
		BytesWrote += sizeof(Data);
		return BytesWrote;
	}else if(Type == CDataBool::GetRegisteredType()){
		SSI_BOOL Data = ((CDataBool*)In)->Data;
		memcpy(thePtr+BytesWrote, &Data, sizeof(Data));
		BytesWrote += sizeof(Data);
		return BytesWrote;
	}else if(Type == CDataVoid::GetRegisteredType()){
		// Write Nothing
		return BytesWrote;
	}else{
		return 0;
	}
	return 0;
}

int WriteFunctionAPI(char* thePtr, SSI_FunctionAPI& theAPI){
	CVariable* aVar;
	CData* aData;
	int ctr = 0;
	int Offset = 0;
	if(thePtr == 0) return 0;
	SSI_INT64 Version = theAPI.GetVersion();
	SSI_INT64 NumParams = theAPI.GetNumParams();
	SSI_INT64 StringSize;
	SSI_INT64 WroteBytes = 0;

	memcpy(thePtr+Offset, &Version, sizeof(Version));
	Offset = Offset + sizeof(Version);

	memcpy(thePtr+Offset, &NumParams, sizeof(NumParams));
	Offset = Offset + sizeof(NumParams);

	StringSize = theAPI.m_FunctionName.GetLength();
	memcpy(thePtr+Offset, &StringSize, sizeof(StringSize));
	Offset = Offset + sizeof(StringSize);
	
	memcpy(thePtr+Offset, theAPI.m_FunctionName.Str(), theAPI.m_FunctionName.GetLength());
	Offset = Offset + StringSize;

	// Write the Return Value
  WroteBytes = 0;
	aVar = theAPI.GetReturnData();
	if(!aVar) return 0;
	aData = &aVar->GetWorkingData();
	if(!aData) return 0;
	WroteBytes = WriteDataObject(thePtr+Offset, aData);
	if(!WroteBytes){
		return 0;
	}
	Offset = Offset + WroteBytes;
	
	//Read the Return Status
	SSI_INT64 RetState = theAPI.m_ReturnStatus.GetState();
	memcpy(thePtr+Offset, &RetState, sizeof(RetState));
	Offset = Offset + sizeof(RetState);
	
	CStatus aStat = theAPI.m_ReturnStatus;
	String s = aStat.GetStatusMessage();
	StringSize = s.GetLength();
	memcpy(thePtr+Offset, &StringSize, sizeof(StringSize));
	Offset = Offset + sizeof(StringSize);
	
	memcpy(thePtr+Offset, s.Str(), StringSize);
	Offset = Offset + StringSize;

	while(ctr < NumParams){
		WroteBytes = 0;
		aVar = theAPI[ctr];  
		if(!aVar) return 0;
		aData = &aVar->GetWorkingData();
		if(!aData) return 0;
		WroteBytes = WriteDataObject(thePtr+Offset, aData);
		if(!WroteBytes){
			return 0;
		}
		Offset = Offset + WroteBytes;
		ctr++;
	}
	return 1;
}

int ReadFunctionAPI(char* thePtr, SSI_FunctionAPI& theAPI){
	CData* aData;

	SSI_INT64 ctr = 0;
	int Offset = 0;
	if(thePtr == 0) return 0;
	SSI_INT64 Version = 0;
	SSI_INT64 NumParams = 0;
	SSI_INT64 StringSize;
	SSI_INT64 RetState = 0;
	SSI_INT64 ReadBytes = 0;

	memcpy(&Version, thePtr+Offset, sizeof(Version));
	Offset = Offset + sizeof(Version);
	memcpy(&NumParams, thePtr+Offset, sizeof(NumParams));
	Offset = Offset + sizeof(NumParams);

	memcpy(&StringSize, thePtr+Offset, sizeof(StringSize));
	Offset = Offset + sizeof(StringSize);
	
	char* str = new char[StringSize+1];
	memcpy(str, thePtr+Offset, StringSize);
	Offset = Offset + StringSize;
	str[StringSize] = 0;
	theAPI.m_FunctionName = str;
	delete [] str;

	// Read the Return Value
	aData = ReadDataObject(thePtr+Offset, ReadBytes);
	if(!aData){
		return 0;
	}
	Offset = Offset + ReadBytes;

	theAPI.GetReturnData()->SetType(aData->Type);
	CStatus aStat;
	if(!(aStat = theAPI.SetReturnData(aData))){
    delete aData;
		return 0;
		//MessageBox(SSI_NULL, aStat.GetStatusMessage().Str(), "Info", MB_OK);
	}

  // Because a copy is made in SetReturnData
  if(aData){
    delete aData;
    aData = 0;
  }

	// Read the Return Status
	memcpy(&RetState, thePtr+Offset, sizeof(RetState));
	Offset = Offset + sizeof(RetState);
	
	memcpy(&StringSize, thePtr+Offset, sizeof(StringSize));
	Offset = Offset + sizeof(StringSize);

	str = new char[StringSize+1];
	memcpy(str, thePtr+Offset, StringSize);
	Offset = Offset + StringSize;
	str[StringSize] = 0;
	theAPI.m_ReturnStatus.Set((SSI_BYTE)RetState, str);
	delete [] str;

	theAPI.InitParams(NumParams);
	while(ctr < NumParams){
		ReadBytes = 0;
		CVariable aVar;
		
		aData = ReadDataObject(thePtr+Offset, ReadBytes);
		if(!aData) return 0;
		Offset = Offset + ReadBytes;
		aVar.SetType(aData->Type);
		aVar.SetData(aData);

    // Because SetData makes a copy
    if(aData){
      delete aData;
      aData = 0;
    }

		if(!theAPI.AddParameter(aVar, ctr)){
			return 0;
		}
		
		ctr++;
	}

	return 1;
}



void TestGetMem(){

	CMemoryMapper aMap;
	char* aptr = (char*)aMap.GetPointer();

	if(!aptr){
		//MessageBox(SSI_NULL, "Bad 1", "Bad 1", MB_OK);
	}else if(aptr[0] == 0){
		//MessageBox(SSI_NULL, "Bad 2", "Bad 2", MB_OK);
	}

	String* s = (String*)aptr;
	//MessageBox(SSI_NULL, (LPCTSTR)s->Str(), "Contents", MB_OK);
	aMap.UnMap(aptr);
}

// Deprecated...
BOOL WINAPI MINION_DlgProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
	if(USE_MINION_WIN){
	  switch(uMsg)
	  {
		// I dont think this is valid anymore
	  //case WM_CLOSE:
		//MinionLog("Closing");
		//  MINION_OnClose(hWnd);
		//  return FALSE;  // Supposed to return 0
	  //case WM_APP:
		//  break;
	  case WM_NULL:
		  break;
	  case WM_COMMAND:
		  if(wParam == MINION_DOFUNCTION){
				if(bMINION_MSG_FLAGS & FLG_FUNC_TYPE){
					return 0;
				}

				bMINION_MSG_FLAGS = bMINION_MSG_FLAGS | FLG_FUNC_TYPE;

				// OK, this needs to be here because it sucks
				CMemoryMapper aMap;
				char* aptr = (char*)aMap.GetPointer();

				if(!aptr){
					MinionLog("Bad aptr");
					return 0;
				}

				MINION_FunctionAPI newAPI;
				CVariable* theVar = new CVariable;
				newAPI.SetReturnData(theVar);

				MinionLog("function call received");

				while(bALL_DATA_RECEIVED < 5){
					Sleep(10);
					MinionLog("Sleeping...");
				}

				if(bALL_DATA_RECEIVED == 5){
					MinionLog("All Data Here");
				}else if(bALL_DATA_RECEIVED > 5){
					MinionLog("Greater Data Here");
				}else{
					MinionLog("Less Data Here");
				}

				CDataString* ClassData;
				CDataString* ExtraData;

				SSI_INT64 BytesRead = 0;
				ClassData = (CDataString*)ReadDataObject(aptr, BytesRead);
				if(!ClassData){
					MessageBox(0, "Phantom Error", "Minion Internal Error 1", MB_OK);   // KEEP MSG
					aMap.UnMap(aptr);
					delete theVar;
					return 0;
				}

				SSI_INT64 TMP;
				ExtraData = (CDataString*)ReadDataObject(aptr+BytesRead, TMP);
				BytesRead += TMP;
				if(!ExtraData){
					MessageBox(0, "Phantom Error", "Minion Internal Error 3", MB_OK);   // KEEP MSG
					aMap.UnMap(aptr);
					delete theVar;
					return 0;
				}


				if(!ReadFunctionAPI(aptr+BytesRead, newAPI)){
					MessageBox(0, "Phantom Error", "Minion Internal Error 2", MB_OK);  // KEEP MSG
					aMap.UnMap(aptr);
					delete theVar;
					return 0;
				}

				aMap.UnMap(aptr);
			
				CFunctionProcessor aProc;
				HWND theWin;
				theWin = (HWND)lParam;
				aProc.FunctionProcessor(newAPI, theWin, ClassData->Data, ExtraData->Data);

				// Must Delete
				delete ClassData;
				ClassData = 0;
				delete ExtraData;
				ExtraData = 0;

				aptr = (char*)aMap.GetPointer();
				WriteFunctionAPI(aptr, newAPI);
				aMap.UnMap(aptr);
				delete theVar;
				MinionLog("Finished Proc");
				bMINION_SYNC = 1;
				return TRUE;

        
/*        
        
        // OK, this needs to be here because it sucks
			  CMemoryMapper aMap;
			  char* aptr = (char*)aMap.GetPointer();

			  if(!aptr){
				  return TRUE;
			  }

			  MINION_FunctionAPI newAPI;
			  CVariable* theVar = new CVariable;
			  newAPI.SetReturnData(theVar);
			  ReadFunctionAPI(aptr, newAPI);
			  aMap.UnMap(aptr);
  			
			  CFunctionProcessor aProc;
			  HWND theWin;
			  theWin = (HWND)lParam;
			  aProc.FunctionProcessor(newAPI, theWin);

			  aptr = (char*)aMap.GetPointer();
			  WriteFunctionAPI(aptr, newAPI);
			  aMap.UnMap(aptr);
			  delete theVar;
        bMINION_SYNC = 1;
			  return TRUE;
*/
		  }else if(wParam == MINION_SETINTTYPE){
			  SSI_INT64 Type = (SSI_INT64)(lParam);
			  CDataInt anInt;
			  DataMap.AddMap(Type, anInt.GetType());
		  }else if(wParam == MINION_SETSTRINGTYPE){
			  SSI_INT64 Type = (SSI_INT64)(lParam);
			  CDataString aString;
			  DataMap.AddMap(Type, aString.GetType());
		  }else if(wParam == MINION_SETREALTYPE){
			  SSI_INT64 Type = (SSI_INT64)(lParam);
			  CDataReal aReal;
			  DataMap.AddMap(Type, aReal.GetType());
		  }else if(wParam == MINION_SETBOOLTYPE){
			  SSI_INT64 Type = (SSI_INT64)(lParam);
			  CDataBool aBool;
			  DataMap.AddMap(Type, aBool.GetType());
		  }else if(wParam == MINION_SETVOIDTYPE){
			  SSI_INT64 Type = (SSI_INT64)(lParam);
			  CDataVoid aVoid;
			  DataMap.AddMap(Type, aVoid.GetType());
		  }
		  //return TRUE;
	  case WM_PACTIVE:{
			  return TRUE;
		  }
	  }
  } // USE_MINION_WIN
	return FALSE;
}

////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////

BOOL WINAPI MinionDllMain(HINSTANCE hinstDll, DWORD fdwReason, LPVOID fImpLoad)
{
	if(fdwReason == DLL_PROCESS_ATTACH)
	{
    MinionLog("Attach to MINION");
		g_hinstDll = hinstDll;
	}
	else if(fdwReason == DLL_PROCESS_DETACH)
	{
    MinionLog("Detach from MINION");
		if(g_hHook != SSI_NULL)
		{
			UnhookWindowsHookEx(g_hHook);
			g_hHook = SSI_NULL;
		}
	}	
	return TRUE;
}

///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////

HWND SetMinionHook(DWORD dwThreadId, int iCreate)
{
	int ctr = 0;
	HWND hMinion = 0;
	if(iCreate)
	{
		// If the global hook was never set, return false
		if(g_hHook != SSI_NULL)
		{
			MinionLog("ghook!=null");
			return 0;
		}

		// Get the current thread, which is Phantom's since this is called from Phantom
		g_dwThreadIdPHANTOM = GetCurrentThreadId(); 

		// Set the hook, with the GetMsgProc, and the *remote* window thread's ID
		g_hHook = SetWindowsHookEx(WH_GETMESSAGE, GetMsgProc, g_hinstDll, dwThreadId);

		if(g_hHook == SSI_NULL)
		{
			DWORD dwErr = GetLastError();
			return 0;
		}

		// Deprecated...
		if(USE_MINION_WIN){
		  if(!CreateMinionWindow(dwThreadId)){
			  return 0;
		  }

		  Sleep(10);
		  // Wait for the window to be created
		  while(!(hMinion = FindWindowEx(SSI_NULL, SSI_NULL, "#32770", "Minion"))){
			  ctr++;
			  if(ctr > 100){
				  MinionLog("minion never found");
				  UnhookWindowsHookEx(g_hHook);
				  return 0;
			  }
			  if(!CreateMinionWindow(dwThreadId)){
				  return 0;
			  }
			  Sleep(50);  // Sleep 10 milliseconds
		  }
    } // USE_MINION_WIN
	}
	else
	{
		if(g_hHook == SSI_NULL)
		{
			return 0;
		}
		
		// Deprecated...
		if(USE_MINION_WIN){
		  SendMessage(hwndMINION, WM_CLOSE, SSI_NULL, SSI_NULL);
  		
		  Sleep(100);
		  if(FindWindowEx(SSI_NULL, SSI_NULL, "#32770", "Minion")){
		  }

		  int ctr = 0;
		  while(FindWindowEx(SSI_NULL, SSI_NULL, "#32770", "Minion") == hwndMINION){
			  SendMessage(hwndMINION, WM_CLOSE, SSI_NULL, SSI_NULL);
			  //PostThreadMessage(dwThreadId, WM_CLOSE, SSI_NULL, SSI_NULL);
			  MinionLog("Trying to close");
			  Sleep(50);
			  ctr++;
			  if(ctr > 10){
				  MinionLog("Close failed");
				  break;
			  }
		  }
    } // USE_MINION_WIN

		if(!UnhookWindowsHookEx(g_hHook)){
			DWORD dwErr = GetLastError();
			String s;
			s.Format("Error: %d", dwErr);
			MinionLog(s.Str());
		}

		g_hHook = 0;
		hMinion = 0;
	}
  if(USE_MINION_WIN){
	  return hMinion;
  }else{
	  return HWND(1);
  }
}

// Deprecated...
int CreateMinionWindow(DWORD dwThreadId){
	if(USE_MINION_WIN){
	  g_CreateMinion = 1;
	  MinionLog("Creating");
	  // Doh!  Yes, we need this!
	  // Post a message to the remote process, which will get caught by the GetMsgProc and create the window
    SetLastError(0);
    DWORD D = GetLastError();
	  if(!PostThreadMessage(dwThreadId, WM_NULL, 0, 0)){
		  MinionLog("No good postmessage");
		  UnhookWindowsHookEx(g_hHook);
		  g_CreateMinion = 0;
		  return 0;
	  }
    D = GetLastError();

	  g_CreateMinion = 0;
  } // USE_MINION_WIN
	return 1;
}


///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////

LRESULT WINAPI GetMsgProc(int nCode, WPARAM IN_wParam, LPARAM IN_lParam)
{
	
  MinionLog("Got Message");

	if(!IN_lParam || nCode < 0 || IN_wParam == PM_NOREMOVE){
		return CallNextHookEx(g_hHook, nCode, IN_wParam, IN_lParam);
	}else{
		MSG* aMsg = (MSG*)IN_lParam;
		if(aMsg->message == WM_COMMAND){
			WPARAM wParam;
			LPARAM lParam;
			wParam = aMsg->wParam;
			lParam = aMsg->lParam;
			if(wParam == MINION_DOFUNCTION){

				if(bMINION_MSG_FLAGS & FLG_FUNC_TYPE){
					return 0;
					//return CallNextHookEx(g_hHook, nCode, IN_wParam, IN_lParam);
				}

				bMINION_MSG_FLAGS = bMINION_MSG_FLAGS | FLG_FUNC_TYPE;

				//aMsg->message = 0; // Clear the message
				//aMsg->wParam = 0;
				//aMsg->hwnd = 0;
				//aMsg->lParam = 0;


				// OK, this needs to be here because it sucks
				CMemoryMapper aMap;
				char* aptr = (char*)aMap.GetPointer();

				if(!aptr){
					MinionLog("Bad aptr");
					return 0;
					//return CallNextHookEx(g_hHook, nCode, IN_wParam, IN_lParam);
				}

				MINION_FunctionAPI newAPI;
				CVariable* theVar = new CVariable;
				newAPI.SetReturnData(theVar);

				MinionLog("function call received");

				while(bALL_DATA_RECEIVED < 5){
					Sleep(10);
					MinionLog("Sleeping...");
				}

				if(bALL_DATA_RECEIVED == 5){
					MinionLog("All Data Here");
				}else if(bALL_DATA_RECEIVED > 5){
					MinionLog("Greater Data Here");
				}else{
					MinionLog("Less Data Here");
				}

				CDataString* ClassData;
				CDataString* ExtraData;
				SSI_INT64 BytesRead = 0;
				ClassData = (CDataString*)ReadDataObject(aptr, BytesRead);
				if(!ClassData){
					MessageBox(0, "Phantom Error", "Minion Internal Error 1", MB_OK);  // KEEP MSG
					aMap.UnMap(aptr);
					delete theVar;
					return 0;
				}

				SSI_INT64 TMP;
				ExtraData = (CDataString*)ReadDataObject(aptr+BytesRead, TMP);
				BytesRead += TMP;
				if(!ExtraData){
					MessageBox(0, "Phantom Error", "Minion Internal Error 3", MB_OK);   // KEEP MSG
					aMap.UnMap(aptr);
					delete theVar;
					return 0;
				}

				
				if(!ReadFunctionAPI(aptr+BytesRead, newAPI)){
					MessageBox(0, "Phantom Error", "Minion Internal Error 2", MB_OK);  // KEEP MSG
					aMap.UnMap(aptr);
					delete theVar;
					return 0;
				}

				aMap.UnMap(aptr);
			
				CFunctionProcessor aProc;
				HWND theWin;
				theWin = (HWND)lParam;
				aProc.FunctionProcessor(newAPI, theWin, ClassData->Data, ExtraData->Data);

				delete ClassData;
				ClassData = 0;
				delete ExtraData;
				ExtraData = 0;

				aptr = (char*)aMap.GetPointer();
				WriteFunctionAPI(aptr, newAPI);
				aMap.UnMap(aptr);
				delete theVar;
				MinionLog("Finished Proc");
				bMINION_SYNC = 1;
				return 0;
				//return CallNextHookEx(g_hHook, nCode, IN_wParam, IN_lParam);
			}else if(wParam == MINION_SETINTTYPE && !(bMINION_MSG_FLAGS & FLG_INT_TYPE)){
				MinionLog("int type received");
				SSI_INT64 Type = (SSI_INT64)(lParam);
				CDataInt anInt;
				DataMap.AddMap(Type, anInt.GetType());
				bALL_DATA_RECEIVED++;
				bMINION_MSG_FLAGS = bMINION_MSG_FLAGS | FLG_INT_TYPE;
				//aMsg->message = 0; // Clear the message
				//aMsg->wParam = 0;
				//aMsg->hwnd = 0;
				return 0;
			}else if(wParam == MINION_SETSTRINGTYPE && !(bMINION_MSG_FLAGS & FLG_STRING_TYPE)){
				MinionLog("string type received");
				SSI_INT64 Type = (SSI_INT64)(lParam);
				CDataString aString;
				DataMap.AddMap(Type, aString.GetType());
				bALL_DATA_RECEIVED++;
				bMINION_MSG_FLAGS = bMINION_MSG_FLAGS | FLG_STRING_TYPE;
				//aMsg->message = 0; // Clear the message
				//aMsg->wParam = 0;
				//aMsg->hwnd = 0;
				return 0;
			}else if(wParam == MINION_SETREALTYPE && !(bMINION_MSG_FLAGS & FLG_REAL_TYPE)){
				MinionLog("real type received");
				SSI_INT64 Type = (SSI_INT64)(lParam);
				CDataReal aReal;
				DataMap.AddMap(Type, aReal.GetType());
				bALL_DATA_RECEIVED++;
				bMINION_MSG_FLAGS = bMINION_MSG_FLAGS | FLG_REAL_TYPE;
				//aMsg->message = 0; // Clear the message
				//aMsg->wParam = 0;
				//aMsg->hwnd = 0;
				return 0;
			}else if(wParam == MINION_SETBOOLTYPE && !(bMINION_MSG_FLAGS & FLG_BOOL_TYPE)){
				MinionLog("bool type received");
				SSI_INT64 Type = (SSI_INT64)(lParam);
				CDataBool aBool;
				DataMap.AddMap(Type, aBool.GetType());
				bALL_DATA_RECEIVED++;
				bMINION_MSG_FLAGS = bMINION_MSG_FLAGS | FLG_BOOL_TYPE;
				//aMsg->message = 0; // Clear the message
				//aMsg->wParam = 0;
				//aMsg->hwnd = 0;
				return 0;
			}else if(wParam == MINION_SETVOIDTYPE && !(bMINION_MSG_FLAGS & FLG_VOID_TYPE)){
				MinionLog("void type received");
				SSI_INT64 Type = (SSI_INT64)(lParam);
				CDataVoid aVoid;
				DataMap.AddMap(Type, aVoid.GetType());
				bALL_DATA_RECEIVED++;
				bMINION_MSG_FLAGS = bMINION_MSG_FLAGS | FLG_VOID_TYPE;
				//aMsg->message = 0; // Clear the message
				//aMsg->wParam = 0;
				//aMsg->hwnd = 0;
				return 0;
			}else{
				return CallNextHookEx(g_hHook, nCode, IN_wParam, IN_lParam);
			}
		}else{
			return CallNextHookEx(g_hHook, nCode, IN_wParam, IN_lParam);
		}
	}

// Deprecated...
  if(USE_MINION_WIN){
	  if(g_CreateMinion){
		  hwndMINION = CreateDialog(g_hinstDll, MAKEINTRESOURCE(IDD_MINION), SSI_NULL, MINION_DlgProc);
		  MinionLog("Created");
	  }
  }

	return CallNextHookEx(g_hHook, nCode, IN_wParam, IN_lParam);
}

int WaitForSync(int Count){
	int ctr = 0;
	while(!bMINION_SYNC){
		Sleep(10);
		ctr++;
		if(ctr > Count){
			return 0;
		}
	}
	MinionLog("Synced...");
	return 1;
}

void ClearMinionSync(){
	bMINION_SYNC = 0;
	bALL_DATA_RECEIVED = 0;
	bMINION_MSG_FLAGS = 0;  // Clear all flags
}

//MINION_API void TypeKeys(String& s, CStatus& Ret){
//	CPhantomWindow aWin;
//	Ret = aWin.TypeKeys(s);
//}

int CMinionApp::ExitInstance()
{
  // TODO: Add your specialized code here and/or call the base class
  //MinionLog("InitInstance MINION");
  return CWinApp::ExitInstance();
}
